import { transform } from 'sucrase'
import $ from 'gogocode'

function compile(source: string, fileName: string, importGraph: Record<string, string[]> = {}) {
  const output = transform(source, {
    filePath: fileName,
    transforms: ['typescript', 'jsx'],
    jsxRuntime: 'automatic',
    jsxImportSource: 'openinula',
    production: true,
  }).code
  return transformImportAndExport(output, fileName, importGraph)
}

const MODULE_KEY = '__modules__'
const DEFAULT_KEY = '__default__'

// transform import and export
export function transformImportAndExport(source: string, fileName: string, importGraph: Record<string, string[]> = {}) {
  const ast = $(source)
  return transformExport(
    transformImport(ast, fileName, importGraph),
    fileName,
  ).generate()
}

// transform import relative path to window.__module__,
// e.g. import a from './a' => const __import__0 = window.__modules__['a']
// e.g. import { a, b } from './a' => const { a, b } = window.__modules__['a']
// e.g. import { a, b as bb } from './a' => const { a, b: bb } = window.__modules__['a']
function transformImport(ast: $.GoGoAST, filName: string, importGraph: Record<string, string[]>) {
  return ast
    .find(`import $_$var from '$_$lib'`)
    .each((item) => {
      const val = item.match.var[0].value
      const lib = item.match.lib[0].value
      if (lib.startsWith('./')) {
        const libName = lib.substring(2)
        pushImportGraph(importGraph, filName, libName)
        item.replaceBy(`
        const ${val} = window.${MODULE_KEY}['${libName}'].default;
        `)
      }
    })
    .root()
    .find(`import { $_$$1 as $_$$2 } from '$_$lib'`)
    .each((item) => {
      const importees = item.match.$1
      const alias = item.match.$2
      const lib = item.match.lib[0].value
      if (lib.startsWith('./')) {
        const libName = lib.substring(2)
        pushImportGraph(importGraph, filName, libName)

        item.replaceBy(`
        const { ${importees
          .map((i, idx) => {
            const alia = alias[idx].value
            const name = i.value
            return alia !== name ? `${name}: ${alia}` : name
          })
          .join(', ')} } = window.${MODULE_KEY}['${libName}'];
        `)
      }
    })
    .root()
    .find(`import { $$$1 } from '$_$lib'`)
    .each((item) => {
      const importees = item.match.$$$1
      const lib = item.match.lib[0].value
      if (lib.startsWith('./')) {
        const libName = lib.substring(2)
        pushImportGraph(importGraph, filName, libName)

        item.replaceBy(`
        const { ${importees
          .map(i => i.imported.name)
          .join(', ')} } = window.${MODULE_KEY}['${libName}'];
        `)
      }
    })
    .root()
}

// transform export to __export__
// e.g. export const a = 1 => const a = 1; __export__(FILENAME, 'a', 1);
// e.g. export const fn = () => 1 => const a = 1; __export__(FILENAME, 'a', () => 1);
// e.g. export function sub() { return 1;} => function sub() { return 1;}; __export__(FILENAME, 'sub', sub);
// e.g. export default { a: 1 } => __export__(FILENAME, 'default', { a: 1 });
// e.g. export class A() { name: 'hhh'} => class A() { name: 'hhh'}; __export__(FILENAME, 'A', A);
function transformExport(ast: $.GoGoAST, fileName: string) {
  return ast
    .find(`export const $_$$1 = $_$$2`)
    .each((item) => {
      const name = item.match.$1[0].value
      item.replaceBy(`
        const ${name} = ${item.match.$2[0].value};
        __export__('${fileName}', '${name}', ${name});
      `)
    })
    .root()
    .find(`export default $_$$1`)
    .each((item) => {
      item.replaceBy(`
        const ${DEFAULT_KEY} = ${item.match.$1[0].value};
        __export__('${fileName}', 'default', ${DEFAULT_KEY});
      `)
    })
    .root()
    .replace(`export function $_$1($$$2) {$$$3}`, `
        function $_$1($$$2) {$$$3};
        __export__('${fileName}', '$_$1', $_$1);
    `)
    .root()
    .replace(`export function $_$1() {$$$2}`, `
        function $_$1() {$$$2};
        __export__('${fileName}', '$_$1', $_$1);
    `)
    .root()
    .replace(`export default function $_$1($$$2) {$$$3}`, `
        function $_$1($$$2) {$$$3};
        __export__('${fileName}', '$_$1', $_$1);
    `)
    .root()
    .replace(`export default function $_$1() {$$$2}`, `
        function $_$1() {$$$2};
        __export__('${fileName}', '$_$1', $_$1);
    `)
    .root()
    .replace(`export class $_$1 {$$$2}`, `
        class $_$1 {$$$2}
        __export__('${fileName}', '$_$1', $_$1);
    `)
    .root()
    .find(`export default class $_$$1 { $_$$2 }`)
    .each((item) => {
      const name = item.match.$1[0].value
      item.replaceBy(`
        class ${name} { ${item.match.$2[0].value} }
        __export__('${fileName}', 'default', ${name});
      `)
    })
    .root()
}

function pushImportGraph(importGraph: Record<string, string[]>, fileName: string, lib: string) {
  const importers = importGraph[fileName]
  if (importers)
    importers.push(lib)
  else
    importGraph[fileName] = [lib]
}

export { compile }
